﻿using System;
using System.IO;
using System.Runtime.Serialization;
using System.Runtime.Serialization.Formatters.Binary;
using System.Text;
using MyOS.ViewModel;

namespace MyOS.Utils
{
    public static class DiskUtils
    {
        public static byte[] FileBytes;
        public static string file;

        #region 用于在第一次创建磁盘时为磁盘首部的DBR写入参数 setDbrForCreate

        public static void setDbrForCreate(FileStream diskFileStream, char diskName)
        {
            IniUtils.WriteInteger("Disk" + diskName, "Size", Convert.ToInt32(diskFileStream.Length), "./config.ini");
            IniUtils.WriteInteger("Disk" + diskName, "RootSize", 5, "./config.ini");


            diskFileStream.Seek(0, SeekOrigin.Begin);
            diskFileStream.WriteByte(Convert.ToByte(64));
            IniUtils.WriteInteger("Disk" + diskName, "BlockSize", 64, "./config.ini");
            //第一个Byte写入每盘块的大小

            diskFileStream.Seek(1, SeekOrigin.Begin);
            diskFileStream.WriteByte(Convert.ToByte(decimal.Ceiling(diskFileStream.Length / 64)));
            IniUtils.WriteInteger("Disk" + diskName, "BlockCount", (int) decimal.Ceiling(diskFileStream.Length / 64),
                "./config.ini");
            //第二个为共多少盘块，要向上取整

            diskFileStream.Seek(2, SeekOrigin.Begin);
            diskFileStream.WriteByte(Convert.ToByte(0));
            IniUtils.WriteInteger("Disk" + diskName, "RecordCount", 0, "./config.ini");
            //FAT表中记录数，等于根目录目录个数

            diskFileStream.Seek(3, SeekOrigin.Begin);
            diskFileStream.WriteByte(Convert.ToByte(diskName));
            IniUtils.WriteString("Disk" + diskName, "VolumeLabel", diskName + "", "./config.ini");
            //卷标

            diskFileStream.Seek(4, SeekOrigin.Begin);
            diskFileStream.WriteByte(Convert.ToByte(1 + decimal.Ceiling(diskFileStream.Length / 4096)));
            IniUtils.WriteInteger("Disk" + diskName, "RootOffset",
                1 + (int) decimal.Ceiling(diskFileStream.Length / 4096), "./config.ini");
            //根目录偏移量

            IniUtils.WriteInteger("Disk" + diskName, "FileStart",
                6 + (int) decimal.Ceiling(diskFileStream.Length / 4096), "./config.ini");
            //文件块开始位置

            IniUtils.WriteString("Disk" + diskName, "CreateTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                "./config.ini");
            IniUtils.WriteString("Disk" + diskName, "ModifyTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                "./config.ini");
            IniUtils.WriteString("Disk" + diskName, "AccessTime", DateTime.Now.ToString("yyyy-MM-dd HH:mm:ss"),
                "./config.ini");
            //将创建时间、修改时间、访问时间存入配置文件
            diskFileStream.Close();
        }

        #endregion

        #region 查找空盘块，参数为文件的大小。若盘块不够返回-1

        public static int findEmptyBlock(char diskName, int fileSize)
        {
            var start = IniUtils.ReadInteger("Disk" + diskName, "FileStart", 7, "./config.ini");
            //默认值 7 为 根目录5+Dbr1+FAT最小大小1
            var fileStream = new FileStream("./Disk" + diskName, FileMode.Open);
            var fatBytes = new byte[(start - 6) * 64]; //fat表大小，文件开始处-dbr1-根目录5
            fileStream.Seek(64, SeekOrigin.Begin);
            fileStream.Read(fatBytes, 0, fatBytes.Length); //从dbr之后1个块开始读
            var emptyDiskCount = 0;
            var beginBlock = -1;
            for (var i = 0; i < fatBytes.Length - 1; i++)
                if (fatBytes[i] == 0) //0为空
                {
                    if (beginBlock == -1) //寻找起始位置
                        beginBlock = i;

                    emptyDiskCount++; //记录多少空盘
                }

            fileStream.Close();
            if (fileSize > emptyDiskCount * 64)
                return -1;
            return beginBlock;
        }

        #endregion

        #region 把文件属性写入根目录

        public static void writeToRoot(char diskName, byte[] attrBytes)
        {
            var start = IniUtils.ReadInteger("Disk" + diskName, "RootOffset", 2, "./config.ini");
            var FileCount = IniUtils.ReadInteger("Disk" + diskName, "RecordCount", 0, "./config.ini");
            var fileStream = new FileStream("./Disk" + diskName, FileMode.Open);
            fileStream.Seek(start * 64, SeekOrigin.Begin);
            var rootBytes = new byte[320];
            fileStream.Read(rootBytes, 0, 320);
            for (var i = 0; i < rootBytes.Length; i += 20)
                if (rootBytes[i] == 0)
                {
                    for (var j = 0; j < 20; j++, i++) rootBytes[i] = attrBytes[j];

                    FileCount++;
                    fileStream.Seek(2, SeekOrigin.Begin);
                    fileStream.WriteByte(Convert.ToByte(FileCount));
                    IniUtils.WriteInteger("Disk" + diskName, "RecordCount", FileCount, "./config.ini");
                    //更改ini表
                    break;
                }

            fileStream.Seek(start * 64, SeekOrigin.Begin);
            fileStream.Write(rootBytes);
            fileStream.Close();
        }

        #endregion

        #region 写入FAT表，同时写入文件

        public static void writeToFatAndDisk(char diskName, byte[] fileBytes, int startBlock)
        {
            var fileStart = IniUtils.ReadInteger("Disk" + diskName, "FileStart", 2, "./config.ini");
            var fileStream =
                new FileStream("./Disk" + diskName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read);
            fileStream.Seek(64, SeekOrigin.Begin);
            var fatBytes = new byte[(fileStart - 6) * 64]; //先将FAT表写入缓存
            fileStream.Read(fatBytes, 0, (fileStart - 6) * 64);
            fileStream.Seek(fileStart * 64, SeekOrigin.Begin);
            var j = 0;
            var before = startBlock;
            for (var i = startBlock; i < fatBytes.Length - 1; i++) //最后一个不用，其编号用作文件结束符//i为fat表项，j为磁盘块角标，k为文件块角标
            {
                if (j >= fileBytes.Length) //判断数据是否写完
                {
                    fatBytes[before] = 255;
                    fileStream.Seek(64, SeekOrigin.Begin);
                    fileStream.Write(fatBytes);
                    fileStream.Close();
                    break;
                }

                if (fatBytes[i] == 0) //盘块为空
                {
                    fatBytes[before] = (byte) i; //将上一个fat表项的值设为当前盘块，
                    //代表上一个指向现在的
                    before = i; //将当前盘块号记录
                    fileStream.Seek((fileStart + i) * 64, SeekOrigin.Begin);
                    for (var k = 0; k < 64 && j < fileBytes.Length; k++, j++) //向磁盘块写字节
                        fileStream.WriteByte(fileBytes[j]);
                }
            }
        }

        #endregion

        #region 读取文件

        public static int readFile(char diskName, string fileName)
        {
            var start = IniUtils.ReadInteger("Disk" + diskName, "RootOffset", 2, "./config.ini");
            var attrBytes = new byte[320]; //文件属性
            var fileStream = new FileStream("Disk" + diskName, FileMode.Open);
            fileStream.Seek(64 * start, SeekOrigin.Begin);
            fileStream.Read(attrBytes, 0, 320); //将根目录读入
            var startBlock = -1; //文件起始块
            var fileSizeBytes = new byte[2]; //因为根目录中文件大小是用两个字节存的，所以需要转换为数字
            for (var i = 0; i < 320; i += 20) //先查根目录
            {
                var c = new char[3];
                for (var j = 0; j < 3; j++)
                    if (attrBytes[i + j] == 0)
                        c[j] = ' ';
                    else
                        c[j] = (char) attrBytes[i + j];

                var temp = new string(c); //将文件名进行比对
                if (fileName.Equals(temp.Trim()))
                {
                    if (attrBytes[i + 4] == 0 || attrBytes[i + 4] == 2) //检查文件权限，不可读返回-1
                    {
                        fileStream.Close();
                        return -1;
                    }

                    startBlock = attrBytes[i + 5]; //文件起始盘块
                    fileSizeBytes[0] = attrBytes[i + 6];
                    fileSizeBytes[1] = attrBytes[i + 7];
                    break;
                }
            }

            if (startBlock == -1)
            {
                fileStream.Close();

                return -2; //没找到文件返回-2
            }

            fileStream.Seek(64, SeekOrigin.Begin); //找文件头
            var fileStart = IniUtils.ReadInteger("Disk" + diskName, "FileStart", 1, "./config.ini");

            var fatBytes = new byte[64 * (fileStart - 6)]; //将fat表读入缓存
            fileStream.Read(fatBytes, 0, fatBytes.Length);
            FileBytes = null;
            FileBytes = new byte[BitConverter.ToInt16(fileSizeBytes)]; //新建文件缓存，大小为文件大小


            for (int i = startBlock, j = 0, k = 0; i < fatBytes.Length - 1;)
            {
                fileStream.Seek((i + fileStart) * 64, SeekOrigin.Begin); //调整起始指针
                while (j < FileBytes.Length && k < 64)
                {
                    FileBytes[j] = (byte) fileStream.ReadByte();
                    j++;
                    k++;
                }

                k = 0;
                if (fatBytes[i].Equals(255)) //读到文件尾部
                    break;
                i = fatBytes[i]; //下一个盘块指针
            }

            file = Encoding.Default.GetString(FileBytes, 0, FileBytes.Length);
            fileStream.Close();
            return 0;
        }

        #endregion

        #region 搜索有没有重名的文件或文件夹

        public static int searchIsExists(char diskName, string fileName, char fileType) //存在返回在根目录中的起始块号 不存在返回-1
        {
            var start = IniUtils.ReadInteger("Disk" + diskName, "RootOffset", 2, "./config.ini");
            var recordCount = IniUtils.ReadInteger("Disk" + diskName, "RecordCount", 0, "./config.ini");
            var fileStream = new FileStream("Disk" + diskName, FileMode.Open, FileAccess.Read);
            var attrBytes = new byte[recordCount * 20];
            fileStream.Seek(start * 64, SeekOrigin.Begin);
            fileStream.Read(attrBytes, 0, attrBytes.Length);
            for (var i = 0; i < attrBytes.Length; i += 20)
            {
                var c = new char[3];
                for (var j = 0; j < 3; j++)
                    if (attrBytes[i + j] == 0)
                        c[j] = ' ';
                    else
                        c[j] = (char) attrBytes[i + j];

                var temp = new string(c); //将文件名进行比对
                if (fileName.Equals(temp.Trim()))
                    if (fileType.Equals((char) attrBytes[i + 3]))
                    {
                        fileStream.Close();
                        return i;
                    }
            }

            fileStream.Close();
            return -1;
        }

        #endregion

        #region 删除文件/文件夹

        public static bool delete(char diskName, string fileName, char fileType)
        {
            var fileStream = new FileStream("Disk" + diskName, FileMode.Open);
            var start = IniUtils.ReadInteger("Disk" + diskName, "RootOffset", 2, "./config.ini");
            var count = IniUtils.ReadInteger("Disk" + diskName, "RecordCount", 0, "./config.ini");
            var attrBytes = new byte[count * 20];
            fileStream.Seek(start * 64, SeekOrigin.Begin);
            fileStream.Read(attrBytes, 0, count * 20);
            var startBlock = 0;
            var isExists = false;

            for (var i = 0; i < attrBytes.Length; i += 20)
            {
                var c = new char[3];
                for (var j = 0; j < 3; j++)
                    if (attrBytes[i + j] == 0)
                        c[j] = ' ';
                    else
                        c[j] = (char) attrBytes[i + j];

                var temp = new string(c);
                if (fileName.Equals(temp.Trim()))
                    if (fileType.Equals((char) attrBytes[i + 3]))
                    {
                        startBlock = attrBytes[5];
                        for (var j = 0; j < 20; j++) //将根目录表项删除
                            attrBytes[j + i] = 0;

                        isExists = true;
                        // goto exit;
                    }

                if (isExists) break;
            }

            if (!isExists)
                return false;

            //exit:
            fileStream.Seek(start * 64, SeekOrigin.Begin);
            fileStream.Write(attrBytes, 0, attrBytes.Length);
            var fatBytes = new byte[(start - 1) * 64];
            fileStream.Seek(64, SeekOrigin.Begin);
            fileStream.Read(fatBytes, 0, fatBytes.Length);
            while (startBlock < fatBytes.Length)
            {
                var temp = fatBytes[startBlock];
                fatBytes[startBlock] = 0;
                if (temp == 255) break;

                startBlock = temp;
            }

            fileStream.Seek(64, SeekOrigin.Begin);
            fileStream.Write(fatBytes, 0, fatBytes.Length);
            fileStream.Close();
            return true;
        }

        #endregion

        #region 重命名

        public static bool rename(char diskName, string changedName, string originName, char fileType)
        {
            var fileNameBytes = new byte[3];
            for (var i = 0; i < Encoding.Default.GetBytes(changedName).Length; i++)
                fileNameBytes[i] = Encoding.Default.GetBytes(changedName)[i];
            var fileStart = searchIsExists(diskName, changedName, fileType); //先判断修改后的名字是否存在
            if (fileStart != -1) //存在的返回结果不是-1,不能修改
                return false;

            fileStart = searchIsExists(diskName, originName, fileType); //查找原来的文件的名称的起始盘块


            var fileStream = new FileStream("Disk" + diskName, FileMode.Open);
            var rootStart = IniUtils.ReadInteger("Disk" + diskName, "RootOffset", 2, "./config.ini");
            fileStream.Seek(64 * rootStart + fileStart, SeekOrigin.Begin);
            for (var i = 0; i < fileNameBytes.Length; i++) //根目录起始处+具体文件偏移字节数
                fileStream.WriteByte(fileNameBytes[i]);

            fileStream.Close();
            return true;
        }

        #endregion

        #region 查看磁盘块状态

        public static byte[] readDiskBlockState(char diskName)
        {
            var diskSize = IniUtils.ReadInteger("Disk" + diskName, "BlockCount", 2, "./config.ini");
            var diskState = new byte[diskSize];
            var fileStart = IniUtils.ReadInteger("Disk" + diskName, "FileStart", 2, "./config.ini");
            for (var i = 0; i < fileStart; i++) diskState[i] = 2; //2为系统占用

            var fatBytes = new byte[(fileStart - 6) * 64];
            var fileStream = new FileStream("Disk" + diskName, FileMode.Open);
            fileStream.Seek(64, SeekOrigin.Begin);
            fileStream.Read(fatBytes, 0, fatBytes.Length);
            for (var i = 0; i < fatBytes.Length - fileStart; i++)
                if (fatBytes[i] == 0)
                    diskState[i + fileStart] = 0; //等于0表示空
                else
                    diskState[i + fileStart] = 1; //不是0就已经被占用

            return diskState;
        }

        #endregion

        #region 读取文件属性

        public static byte[] readFileAttribute(char diskName, int Offset)
        {
            var fileStream = new FileStream("Disk" + diskName, FileMode.Open);
            var rootStart = IniUtils.ReadInteger("Disk" + diskName, "RootOffset", 2, "./config.ini");
            var attrBytes = new byte[20];
            fileStream.Seek(64 * rootStart + Offset, SeekOrigin.Begin); //定位到文件的起始位置
            var i = 0;
            while (i < 20) attrBytes[i++] = (byte) fileStream.ReadByte();
            fileStream.Close();
            return attrBytes;
        }

        #endregion

        #region 保存对象

        public static void saveObjectToFile(FileViewModel ViewModel)
        {
            IFormatter formatter = new BinaryFormatter();
            var createFileStream = new FileStream("objMenuItem", FileMode.Create, FileAccess.Write);
            formatter.Serialize(createFileStream, ViewModel);
            createFileStream.Close();
        }

        #endregion
    }
}